<?php
set_time_limit(0);
ini_set('memory_limit', '128M');

$daemon = new Daemon();
$daemon->main(2);

class Daemon {

    public $map = array();

    public $id;

    public function __construct() {
        pcntl_signal(SIGUSR1, array($this, 'sigHandler'));
        pcntl_signal(SIGCLD, array($this, 'sigHandler'));
        pcntl_signal(SIGCHLD, array($this, 'sigHandler'));
    }

    public function main($num) {
        $pid = pcntl_fork();
        if ($pid) {
            exit;
        } elseif ($pid == 0) {
            $this->getFork($num);
        }
    }

    public function getFork($num, $id = 0) {
        while ($num) {
            if (!$id) {
                $this->id = $num;
            } else {
                $this->id = $id;
            }
            $num --;
            $pid = pcntl_fork();
            if (!$pid) {
                $this->childProcess($this->id);
            } else {
                $this->map[$this->id] = $pid;
            }
        }

        if ($this->map && !$id) {
            $this->parentProcess();
        }
    }

    public function parentProcess() {
        var_dump($this->map);
        while(1) {
            // 让父进程一直在执行信号量处理器，却不退出
            pcntl_signal_dispatch();
        }
        // 如果子进程退出了，重新执行
        // 这里重新执行的话父进程的 ID 不会变，只会创建新的子进程
        echo 'PARTENT MEMORY: ' . memory_get_usage() . PHP_EOL;
        exit;
    }

    public function childProcess($id) {
        echo $this->id . PHP_EOL;
        $pid = getmypid();
        $file = sprintf('/tmp/process/%s.pid', $pid);
        touch($file);

        $start = time();
        $i = 0;
        while(1) {
            $a[$i++] = str_repeat('*', 10);
            if ((time() - $start) >= 5) {
                // echo 'CHILD MEMORY: ' . memory_get_usage() . PHP_EOL;
                break;
            }
        }
        // 发送自定义信号量
        unlink($file);
        posix_kill(posix_getppid(), SIGUSR1);
        exit;
    }

    public function sigHandler($sig) {
        switch ($sig) {
        case SIGUSR1:
            $this->_checkProcess();
            break;
        default:
            break;
        }
        pcntl_wait($status);
    }

    private function _checkProcess() {
        var_dump($this->map);
        // 判断终止掉的是哪个进程,供父进程的信号处理器调用
        foreach ($this->map as $id => $pid) {
            $file = sprintf('/tmp/process/%s.pid', $pid);
            echo 'doCheck ' . $file . PHP_EOL;
            // 检查对应的 pid 是否存在
            if (file_exists($file) === false) {
                echo sprintf('#%dProcessNotFound', $pid);
                $this->getFork(1, $id);
            }
        }
    }
}
